/**
 *******************************************************************************************************************************************
 * @file        ui_event.c
 * @brief       UI 页面事件回调
 * @details     /
 * @since       Change Logs:
 * Date         Author       Notes
 * 2022-12-08   lzh          the first version
 * 2022-12-21   lzh          重构
 * 2023-02-23   lzh          精简注释
 * @remark      /
 *******************************************************************************************************************************************
 * @attention
 *
 * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS WITH CODING INFORMATION
 * REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. AS A RESULT, SYNWIT SHALL NOT BE HELD LIABLE
 * FOR ANY DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE CONTENT
 * OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING INFORMATION CONTAINED HEREIN IN CONN-
 * -ECTION WITH THEIR PRODUCTS.
 *
 * @copyright   2012 Synwit Technology
 *******************************************************************************************************************************************
 */
#include "ui_event.h"

/*******************************************************************************************************************************************
 * Private Define
 *******************************************************************************************************************************************/
/* CE-48Pin 只有 2MB SDRAM, 空间不足以一次性容纳 一组 GIF 图 */
#if (SWM34S_LCM_PCBV == SWM34SCE_PIN48_A2)
	/**
	 * GIF 显示模式:
	 *
	 * 0 - 按图片组预先加载至SDRAM, 避免与avi视频流竞争jpeg硬件解码器;
	 *
	 * 1 - 从 spi->sdram 加载图片，加载一张显示一张，
	 */
	#define GIF_SHOW_MODE 		1

	/** LVGL 字体结构变量< lv_font_t > */
	#define USER_FONT 			demo_font

	/** 对应字体加载至内存的起始地址 */
	#define USER_FONT_ADDR 		Demo_Font_Addr

#else
	/**
	 * GIF 显示模式:
	 *
	 * 0 - 按图片组预先加载至SDRAM, 避免与avi视频流竞争jpeg硬件解码器;
	 *
	 * 1 - 从 spi->sdram 加载图片，加载一张显示一张，
	 */
	#define GIF_SHOW_MODE 		    ( (LCD_HDOT >= 800 && LCD_VDOT >= 480) ? 1 : 0 )

    #if (LCD_FMT == LCD_ARGB888 && (LCD_HDOT >= 480 && LCD_VDOT >= 480) )
        /** LVGL 字体结构变量< lv_font_t > */
        #define USER_FONT 			demo_font
        /** 对应字体加载至内存的起始地址 */
        #define USER_FONT_ADDR 		Demo_Font_Addr
    
    #else 
        /** LVGL 字体结构变量< lv_font_t > */
        #define USER_FONT 			yahei11_font
        /** 对应字体加载至内存的起始地址 */
        #define USER_FONT_ADDR 		Yahei11_Font_Addr
    #endif
#endif

/** <简便写法>仅用于播放某固定音源(=0 单击 / >0 滑动) */
#define PLAY_MUSIC(flag)                                                                       \
    do                                                                                         \
    {                                                                                          \
        char file_path[FF_MAX_LFN];                                                            \
        strcpy(file_path, VolumeStr[SPI]);                                                     \
        strcat(file_path, File_Table.file[(flag == 0) ? FT_MUSIC_ICON : FT_MUSIC_SLIDE].name); \
        mv_task_init(file_path);                                                               \
    } while (0)

/* 字体变量声明引用 */
LV_FONT_DECLARE(USER_FONT);
extern uint32_t USER_FONT_ADDR;
	
/*******************************************************************************************************************************************
 * Private Constant
 *******************************************************************************************************************************************/
/** M0/4 GIF 任务执行周期(图片刷新频率 MS) */
static const uint32_t Task_Ms_M0_M4 = 2000;
/** PD GIF 任务执行周期(图片刷新频率 MS) */
static const uint32_t Task_Ms_PD = 1000;
/** PP GIF 任务执行周期(图片刷新频率 MS) */
static const uint32_t Task_Ms_PP = 150;

/*******************************************************************************************************************************************
 * Private Variable
 *******************************************************************************************************************************************/
/** 本地 UI 全局句柄 */
static lv_ui *const LU = &GUI;

/*******************************************************************************************************************************************
 * Private Prototype
 *******************************************************************************************************************************************/
static void gif_task_pd(lv_task_t *t);
static void gif_task_pp(lv_task_t *t);
static void gif_task_m0(lv_task_t *t);
static void gif_task_m4(lv_task_t *t);

/*******************************************************************************************************************************************
 * Private Function
 *******************************************************************************************************************************************/
/**
 * @brief   清理首页的视频
 */
static void page_clean_home_video(void)
{
    uint32_t video_loop_time = 0, video_loop_max = 0;
    video_get_loop(&video_loop_time, &video_loop_max);
    if (1 == video_loop_max) /* 仅 首页 置为 1 */
    {
        video_set_loop(0, 0xFFFFFFFF);
        mv_task_over();
    }
}

/**
 * @brief   进入 PD 页前的资源准备
 */
static void page_ready_pd(void)
{
    LU->scr_home_tab_pd_img_gif = lv_img_create(LU->scr_home_tab_pd, NULL);
    GIF.obj = LU->scr_home_tab_pd_img_gif;
    GIF.i = 0;
    GIF.i_max = 0;
    GIF.task = lv_task_create(gif_task_pd, Task_Ms_PD, LV_TASK_PRIO_MID, NULL);
    lv_task_ready(GIF.task);
}

/**
 * @brief   退出 PD 页前的清理、释放资源
 */
static void page_clean_pd(void)
{
    delete_gif(&GIF);
}

/**
 * @brief   进入 PP 页前的资源准备
 */
static void page_ready_pp(void)
{
    /* 加载中文字库bin文件(必须在设置样式前完成该动作) */
    if (0 == load_source_to_sdram(SPI, File_Table.file[FT_FONT_CHINA].name, BIN, &USER_FONT_ADDR, sdram_malloc))
    {
        /* 字体样式-中文字库 */
        lv_style_set_text_font(&Style_Label_China, LV_STATE_DEFAULT, &USER_FONT);
        lv_obj_set_hidden(LU->scr_home_tab_pp_label_top, false);
        lv_obj_set_hidden(LU->scr_home_tab_pp_label_mid, false);
        /* 刷新所有应用了此样式的控件 */
        lv_obj_report_style_mod(&Style_Label_China);

#if 1 // FONT_SCROLL_TOP
        lv_obj_set_size(LU->scr_home_tab_pp_label_top,
                        GET_X(2, 0, ICON_HB_W, GET_OFFSET_X(LV_HOR_RES_MAX, ICON_HB_W, 3)) - GET_X(0, 0, ICON_HB_W, GET_OFFSET_X(LV_HOR_RES_MAX, ICON_HB_W, 3)) - ICON_HB_W,
                        ICON_HB_H * 9 / 10);
        /* 相对父级顶部居中对齐 */
        lv_obj_align(LU->scr_home_tab_pp_label_top, NULL, LV_ALIGN_IN_TOP_MID, 0,
                     (GET_X(2, 0, ICON_HB_W, GET_OFFSET_X(LV_HOR_RES_MAX, ICON_HB_W, 3)) - GET_X(0, 0, ICON_HB_W, GET_OFFSET_X(LV_HOR_RES_MAX, ICON_HB_W, 3)) - ICON_HB_W > lv_obj_get_width(LU->scr_home_tab_pp_label_top))
                         ? ICON_HB_H
                         : 0);

        lv_obj_align(LU->scr_home_tab_pp_label_mid, NULL, LV_ALIGN_CENTER, 0, 0);
#endif
    }

#if (GIF_SHOW_MODE == 0)
    for (uint32_t i = PP_GIF_START; i < PP_GIF_START + PP_GIF_SIZE; ++i)
    {
        if (0 != load_source_to_sdram(SPI, Image_Table[i].file->name, IMG, Image_Table[i].img, sdram_malloc))
            break;
    }
#endif

    /* video_setting */
    PLAY_MV(GET_OFFSET_X(LV_HOR_RES_MAX, FLASH_VIDEO_AREA_W, 1), GET_OFFSET_Y(LV_VER_RES_MAX, FLASH_VIDEO_AREA_H, 1));
}

/**
 * @brief   退出 PP 页前的清理、释放资源
 */
static void page_clean_pp(void)
{
    delete_gif(&GIF);
    /* video_setting */
    mv_task_over();

#if (GIF_SHOW_MODE == 0)
    for (uint32_t i = PP_GIF_START; i < PP_GIF_START + PP_GIF_SIZE; ++i)
    {
        if (Image_Table[i].img->data != NULL || Image_Table[i].img->data_size != 0)
        {
            if (0 == remove_source_to_sdram(IMG, (void *)Image_Table[i].img, sdram_free))
            {
                Image_Table[i].img->data = NULL;
                Image_Table[i].img->data_size = 0;
            }
        }
    }
#endif

    /* 释放中文字库所占用的内存空间 */
    if (USER_FONT_ADDR != 0)
    {
        remove_source_to_sdram(BIN, (void *)&USER_FONT_ADDR, sdram_free);
        USER_FONT_ADDR = 0;
    }
    /* 释放掉字库, 故隐藏字体显示 */
    lv_obj_set_hidden(LU->scr_home_tab_pp_label_top, true);
    lv_obj_set_hidden(LU->scr_home_tab_pp_label_mid, true);
}

/**
 * @brief   进入 M0 / M4 SHOW 页前的资源准备
 * @param   user_data : 用户自定义数据(被按下的按钮索引-可用来对应的具体芯片型号轮播图)
 * @retval  /
 */
static void page_ready_m0_m4(uint8_t user_data)
{
#if (OPTION_SET_BG == 1) /* 进入 M0 / M4 SHOW 页是否使用图片当背景 */
    if (0 == load_source_to_sdram(SPI, Image_Table[IMG_WALLPAPER].file->name, IMG, Image_Table[IMG_WALLPAPER].img, sdram_malloc))
    {
#endif

#if (OPTION_SET_BG > 0)
        /* Write codes scr_m0_m4_img_bg */
        LU->scr_m0_m4_img_bg = lv_img_create(LU->scr_m0_m4, NULL);
        lv_obj_set_pos(LU->scr_m0_m4_img_bg, 0, 0);
        lv_img_set_src(LU->scr_m0_m4_img_bg, Image_Table[IMG_WALLPAPER].img);
        /*
        lv_img_set_pivot(LU->scr_m0_m4_img_bg, 0, 0);
        lv_img_set_angle(LU->scr_m0_m4_img_bg, 0);
        lv_obj_set_click(LU->scr_m0_m4_img_bg, true);
        */

        /* 发送到后台作背景 */
        lv_obj_move_background(LU->scr_m0_m4_img_bg);

        /* 需要在退出页面时释放
        remove_source_to_sdram(IMG, (void *)Image_Table[IMG_WALLPAPER].img, sdram_free);
        LOG_INFO("[remove] img_addr = 0x[%p]\r\n", Image_Table[IMG_WALLPAPER].img->data);
        */

#endif

#if (OPTION_SET_BG == 1)
    }
#endif

    static uint8_t static_index = 0;
    static_index = user_data;

    /* video_setting */
    PLAY_MV(GET_OFFSET_X(LV_HOR_RES_MAX, FLASH_VIDEO_AREA_W, 1),
            GET_OFFSET_Y(LV_VER_RES_MAX, FLASH_VIDEO_AREA_H, 1));

    lcd_set_alpha(LAYER_2_VIDEO, 0xFF / 100 * lv_slider_get_value(LU->scr_m0_m4_slider_video));
    
    /* gif_setting */
    lv_task_cb_t task_cb = NULL;

    if (LU->scr_switch_flag == M0 || LU->scr_switch_flag == M0_SHOW)
        task_cb = gif_task_m0;
    else if (LU->scr_switch_flag == M4 || LU->scr_switch_flag == M4_SHOW)
        task_cb = gif_task_m4;
    else
        return;

    LU->scr_m0_m4_img_gif = lv_img_create(LU->scr_m0_m4, NULL);
    GIF.obj = LU->scr_m0_m4_img_gif;
    GIF.i = 0;
    GIF.i_max = 0;
    GIF.task = lv_task_create(task_cb, Task_Ms_M0_M4, LV_TASK_PRIO_MID, &static_index);
    lv_task_ready(GIF.task);
}

/**
 * @brief   退出 M0 / M4 SHOW 页前的清理、释放资源
 */
static void page_clean_m0_m4(void)
{
    delete_gif(&GIF);
    /* video_setting */
    Video_Touch_Flag = 0;
    lcd_set_alpha(LAYER_2_VIDEO, 0xFF);
    mv_task_over();
    

#if (OPTION_SET_BG == 1) /* 进入 M0 / M4 SHOW 页是否使用图片当背景 */
    /* 需要在退出页面时释放 */
    remove_source_to_sdram(IMG, (void *)Image_Table[IMG_WALLPAPER].img, sdram_free);
    LOG_INFO("[remove] img_addr = 0x[%p]\r\n", Image_Table[IMG_WALLPAPER].img->data);
#endif

    lv_obj_del(LU->scr_m0_m4);
    LU->scr_m0_m4 = NULL;
}

//========================================================Screen_Home========================================================//
/**
 * @brief   tabview 的事件回调
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_tabview(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_VALUE_CHANGED) /* 在通过滑动或单击选项卡按钮选择新选项卡时发送 */
    {
        uint16_t act = lv_tabview_get_tab_act(obj);
        if (act > lv_tabview_get_tab_count(obj))
            return; /* 活跃页面索引无效, 超出限定范围 */

        switch (act)
        {
        case (HOME): // break;//continue
        case (M0):   // break;//continue
        case (M4):
            page_clean_home_video();       /* 清理首页视频 */
            PLAY_MUSIC(1);                 /* 滑动音效 */
            if (PD == LU->scr_switch_flag) /* 如果是从 PD 页离开, 则需要先清理释放资源 */
                page_clean_pd();
            break;

        case (PD):
            if (PP == LU->scr_switch_flag) /* 如果是从 PP 页离开, 则需要先清理释放资源 */
                page_clean_pp();

            /* 跳转前的准备 */
            page_ready_pd();
            PLAY_MUSIC(1); /* 滑动音效 */
            break;

        case (PP):
            if (PD == LU->scr_switch_flag) /* 如果是从 PD 页离开, 则需要先清理释放资源 */
                page_clean_pd();

            /* 跳转前的准备 */
            page_ready_pp();
            break;

        default:
            break;
        }
        /* 跳转 TAB 页
        lv_tabview_set_tab_act(obj, act, LV_ANIM_OFF);
        */

        /* 标记页面 */
        LU->scr_switch_flag = act;
    }
}

/**
 * @brief   tabview 下属各 tab-page 的事件回调
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  /
 */
static void event_cb_tab_page(lv_obj_t *obj, lv_event_t e)
{
    switch (e)
    {
    case (LV_EVENT_DRAG_BEGIN): /* 对象的拖动已经开始 */
        mv_task_pause();
        // LOG_INFO("Page Drag Begin\r\n");
        break;

    case (LV_EVENT_RELEASED): /* 对象已被释放 */
        mv_task_resume();
        // LOG_INFO("Page Press Released\r\n");
        break;
    }
}

/**
 * @brief   主桌面注册事件回调
 * @param   ui : 全局句柄
 * @retval  \
 */
void events_init_scr_home(lv_ui *ui)
{
    /* 注册 tabview 的事件回调 */
    lv_obj_set_event_cb(ui->scr_home_tabview, event_cb_tabview);

    /* 注册 tabview 下属各 tab-page 的事件回调 */
    lv_obj_set_event_cb(ui->scr_home_tab_home, event_cb_tab_page);
    lv_obj_set_event_cb(ui->scr_home_tab_m0, event_cb_tab_page);
    lv_obj_set_event_cb(ui->scr_home_tab_m4, event_cb_tab_page);
    lv_obj_set_event_cb(ui->scr_home_tab_pd, event_cb_tab_page);
    lv_obj_set_event_cb(ui->scr_home_tab_pp, event_cb_tab_page);
}

//========================================================Page_Btn_Home/Back========================================================//
/**
 * @brief   主桌面下的四个页面 Home / Back 键的事件回调( H B )
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_imgbtn_hb(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_RELEASED) /* 任何情况下对象释放了就触发 */
    {
        switch (LU->scr_switch_flag)
        {
        case (HOME):
            break;
        case (M0):
            break;
        case (M4):
            break;

        case (PD):
            page_clean_pd();
            break;

        case (PP):
            page_clean_pp();
            break;

        default:
            break;
        }
        PLAY_MUSIC(0); /* 单击音效 */

        /* 标记页面 */
        LU->scr_switch_flag = HOME;
        /* 跳转 TAB-Home 页 */
        lv_tabview_set_tab_act(LU->scr_home_tabview, LU->scr_switch_flag, LV_ANIM_OFF);
    }
}

//========================================================Page_Home========================================================//
/**
 * @brief   主桌面下 Home 页的 LOGO 按钮回调(视频)
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_imgbtn_logo(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_CLICKED) /* 点击对象, 如果不拖动则在释放时调用（无论是否长按） */
    {
        page_clean_home_video();
    }
}

/**
 * @brief   主桌面下 Home 页四个跳转按钮的事件回调
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_imgbtn_home(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_RELEASED) /* 任何情况下对象释放了就触发 */
    {
        page_clean_home_video();

        /* 单击音效 */
        PLAY_MUSIC(0);

        /* 标记页面 */
        if (LU->scr_home_tab_home_imgbtn_m0 == obj)
        {
            LU->scr_switch_flag = M0;
        }
        else if (LU->scr_home_tab_home_imgbtn_m4 == obj)
        {
            LU->scr_switch_flag = M4;
        }
        else if (LU->scr_home_tab_home_imgbtn_pd == obj)
        {
            LU->scr_switch_flag = PD;

            /* 跳转前的准备  */
            page_ready_pd();
        }
        else if (LU->scr_home_tab_home_imgbtn_pp == obj)
        {
            LU->scr_switch_flag = PP;

            /* 跳转前的准备 */
            page_ready_pp();
        }
        /* 跳转 TAB 页 */
        lv_tabview_set_tab_act(LU->scr_home_tabview, LU->scr_switch_flag, LV_ANIM_OFF);
    }
}

/**
 * @brief   主桌面 Home 页注册事件回调
 * @param   ui : 全局句柄
 * @retval  \
 */
void events_init_home_page_home(lv_ui *ui)
{
    /* 注册 LOGO 按钮的事件回调 */
    lv_obj_set_event_cb(ui->scr_home_tab_home_imgbtn_logo, event_cb_imgbtn_logo);

    /* 注册 四个跳转按钮 的事件回调 */
    lv_obj_set_event_cb(ui->scr_home_tab_home_imgbtn_m0, event_cb_imgbtn_home);
    lv_obj_set_event_cb(ui->scr_home_tab_home_imgbtn_m4, event_cb_imgbtn_home);
    lv_obj_set_event_cb(ui->scr_home_tab_home_imgbtn_pd, event_cb_imgbtn_home);
    lv_obj_set_event_cb(ui->scr_home_tab_home_imgbtn_pp, event_cb_imgbtn_home);
}

//========================================================Page_M0========================================================//
/**
 * @brief   M0 页 跳转 M0_SHOW 页的按钮回调
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_imgbtn_m0(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_RELEASED) /* 任何情况下对象释放了就触发 */
    {
        for (uint8_t i = 0; i < sizeof(LU->scr_home_tab_m0_imgbtn_synwit_m0) / sizeof(LU->scr_home_tab_m0_imgbtn_synwit_m0[i]); ++i)
        {
            if (obj == LU->scr_home_tab_m0_imgbtn_synwit_m0[i])
            {
                setup_scr_m0_m4(LU);

                /* 跳转前的准备 */
                page_ready_m0_m4(i);

                /* 枚举值等间距: M0_SHOW or M4_SHOW 均与 M0 or M4 相差 4 */
                LU->scr_switch_flag += 4;
            }
        }
    }
}

/**
 * @brief   主桌面 M0 页注册事件回调
 * @param   ui : 全局句柄
 * @retval  \
 */
void events_init_home_page_m0(lv_ui *ui)
{
    /* 注册 Home / Back 键的事件回调 */
    lv_obj_set_event_cb(ui->scr_home_tab_m0_imgbtn_home, event_cb_imgbtn_hb);
    lv_obj_set_event_cb(ui->scr_home_tab_m0_imgbtn_back, event_cb_imgbtn_hb);

    /* Write codes scr_home_tab_m0_imgbtn_synwit_m0[] */
    for (uint8_t i = 0; i < sizeof(ui->scr_home_tab_m0_imgbtn_synwit_m0) / sizeof(ui->scr_home_tab_m0_imgbtn_synwit_m0[i]); ++i)
    {
        lv_obj_set_event_cb(ui->scr_home_tab_m0_imgbtn_synwit_m0[i], event_cb_imgbtn_m0);
    }
}

//========================================================Page_M4========================================================//
/**
 * @brief   M4 页 跳转 M4_SHOW 页的按钮回调
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_imgbtn_m4(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_RELEASED) /* 任何情况下对象释放了就触发 */
    {
        for (uint8_t i = 0; i < sizeof(LU->scr_home_tab_m4_imgbtn_synwit_m4) / sizeof(LU->scr_home_tab_m4_imgbtn_synwit_m4[i]); ++i)
        {
            if (obj == LU->scr_home_tab_m4_imgbtn_synwit_m4[i])
            {
                setup_scr_m0_m4(LU);

                /* 跳转前的准备 */
                page_ready_m0_m4(i);

                /* 枚举值等间距: M0_SHOW or M4_SHOW 均与 M0 or M4 相差 4 */
                LU->scr_switch_flag += 4;
            }
        }
    }
}

/**
 * @brief   主桌面 M4 页注册事件回调
 * @param   ui : 全局句柄
 * @retval  \
 */
void events_init_home_page_m4(lv_ui *ui)
{
    /* 注册 Home / Back 键的事件回调 */
    lv_obj_set_event_cb(ui->scr_home_tab_m4_imgbtn_home, event_cb_imgbtn_hb);
    lv_obj_set_event_cb(ui->scr_home_tab_m4_imgbtn_back, event_cb_imgbtn_hb);

    /* Write codes scr_home_tab_m4_imgbtn_synwit_m4[] */
    for (uint8_t i = 0; i < sizeof(ui->scr_home_tab_m4_imgbtn_synwit_m4) / sizeof(ui->scr_home_tab_m4_imgbtn_synwit_m4[i]); ++i)
    {
        lv_obj_set_event_cb(ui->scr_home_tab_m4_imgbtn_synwit_m4[i], event_cb_imgbtn_m4);
    }
}

//========================================================Page_PD========================================================//
/**
 * @brief   主桌面 PD 页注册事件回调
 * @param   ui : 全局句柄
 * @retval  \
 */
void events_init_home_page_pd(lv_ui *ui)
{
    /* 注册 Home / Back 键的事件回调 */
    lv_obj_set_event_cb(ui->scr_home_tab_pd_imgbtn_home, event_cb_imgbtn_hb);
    lv_obj_set_event_cb(ui->scr_home_tab_pd_imgbtn_back, event_cb_imgbtn_hb);
}

//========================================================Page_PP========================================================//
/**
 * @brief   Picture 页视频操作按钮回调 - 左/右(隐藏式)
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_btn_hide(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_CLICKED) /* 点击对象, 如果不拖动则在释放时调用(无论是否长按) */
    {
        if (obj == LU->scr_home_tab_pp_btn_hide[0]) /* Left */
        {
            /* video_setting */
            video_set_post(LAYER_2_VIDEO,
                           0,
                           GET_OFFSET_Y(LV_VER_RES_MAX, FLASH_VIDEO_AREA_H, 1));
            
        }
        else if (obj == LU->scr_home_tab_pp_btn_hide[1]) /* Right */
        {
            /* video_setting */
            video_set_post(LAYER_2_VIDEO,
                           LV_HOR_RES_MAX - FLASH_VIDEO_AREA_W,
                           GET_OFFSET_Y(LV_VER_RES_MAX, FLASH_VIDEO_AREA_H, 1));
            
        }
    }
}

/**
 * @brief   开始/暂停 按钮
 */
static void page_pp_btn_go(void)
{
    if (NULL == GIF.task)
    {
        lv_obj_set_hidden(LU->scr_home_tab_pp_label_mid, true);

        if (NULL == GIF.obj)
        {
            LU->scr_home_tab_pp_img_gif = lv_img_create(LU->scr_home_tab_pp, NULL);
            GIF.obj = LU->scr_home_tab_pp_img_gif;
            uint16_t index = GIF.i;
            index += PP_GIF_START;
            GIF.img = Image_Table[index].img;
            GIF.i_max = PP_GIF_SIZE;
#if GIF_SHOW_MODE
            if (0 == load_source_to_sdram(SPI, Image_Table[index].file->name, IMG, Image_Table[index].img, sdram_malloc))
            {
                remove_source_to_sdram(IMG, (void *)Image_Table[index].img, sdram_free);
#else
            {
#endif
                lv_img_set_src(GIF.obj, GIF.img);
                ++GIF.i;
				/* 相对父级居中对齐 */
                lv_obj_align(GIF.obj, NULL, LV_ALIGN_CENTER, 0, 0);
            }
        }
        GIF.task = lv_task_create(gif_task_pp, Task_Ms_PP, LV_TASK_PRIO_MID, NULL);
        lv_task_ready(GIF.task);
    }
    else
    {
        lv_obj_set_hidden(LU->scr_home_tab_pp_label_mid, false);
        delete_gif(&GIF);
    }
}

/**
 * @brief   上一张 / 下一张 按钮
 * @param   dir : 0-上一张 / 1-下一张
 * @retval  /
 */
static void page_pp_btn_up_down(uint8_t dir)
{
    if (NULL != GIF.task) /* 当前正在播放, 需要打断 */
    {
        lv_task_del(GIF.task);
        GIF.task = NULL;
    }
    if (NULL == GIF.obj)
    {
        LU->scr_home_tab_pp_img_gif = lv_img_create(LU->scr_home_tab_pp, NULL);
        GIF.obj = LU->scr_home_tab_pp_img_gif;
    }
    lv_obj_set_hidden(LU->scr_home_tab_pp_label_mid, true);

    uint16_t index = GIF.i;
    index += PP_GIF_START;
    GIF.img = Image_Table[index].img;
    GIF.i_max = PP_GIF_SIZE;
    GIF.i = (GIF.i < GIF.i_max) ? GIF.i : 0; /* 先判断数据有效性 */

    if (dir == 0)
    {
        if (GIF.i <= 1) /* 判断上一张是否到达边界 */
            GIF.i = (GIF.i == 1) ? (GIF.i_max - 1) : (GIF.i_max - 2);
        else
            GIF.i -= 2;
    }

#if GIF_SHOW_MODE
    if (0 == load_source_to_sdram(SPI, Image_Table[index].file->name, IMG, Image_Table[index].img, sdram_malloc))
    {
        remove_source_to_sdram(IMG, (void *)Image_Table[index].img, sdram_free);
#else
    {
#endif
        lv_img_set_src(GIF.obj, GIF.img);
        if (++GIF.i >= GIF.i_max)
            GIF.i = 0;
		/* 相对父级居中对齐 */
        lv_obj_align(GIF.obj, NULL, LV_ALIGN_CENTER, 0, 0);
    }
}

/**
 * @brief   Picture 页 GIF 操作按钮回调 - 左/中/右
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_imgbtn_gif(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_RELEASED) /* 任何情况下对象释放了就触发 */
    {
        for (uint8_t i = 0; i < sizeof(LU->scr_home_tab_pp_imgbtn_gif) / sizeof(LU->scr_home_tab_pp_imgbtn_gif[i]); ++i)
        {
            if (obj == LU->scr_home_tab_pp_imgbtn_gif[i])
            {
                switch (i)
                {
                case (0): /* Left */
                    page_pp_btn_up_down(0);
                    break;

                case (1): /* Mid */
                    page_pp_btn_go();
                    break;

                case (2): /* Right */
                    page_pp_btn_up_down(1);
                    break;

                default:
                    break;
                }
            }
        }
    }
}

/**
 * @brief   主桌面 PP 页注册事件回调
 * @param   ui : 全局句柄
 * @retval  \
 */
void events_init_home_page_pp(lv_ui *ui)
{
    /** 注册 Home / Back 键的事件回调 */
    lv_obj_set_event_cb(ui->scr_home_tab_pp_imgbtn_home, event_cb_imgbtn_hb);
    lv_obj_set_event_cb(ui->scr_home_tab_pp_imgbtn_back, event_cb_imgbtn_hb);

    /* Write codes scr_home_tab_pp_imgbtn_gif[] */
    for (uint8_t i = 0; i < sizeof(ui->scr_home_tab_pp_imgbtn_gif) / sizeof(ui->scr_home_tab_pp_imgbtn_gif[i]); ++i)
    {
        lv_obj_set_event_cb(ui->scr_home_tab_pp_imgbtn_gif[i], event_cb_imgbtn_gif);
    }

    /* Write codes scr_home_tab_pp_btn_hide[] */
    for (uint8_t i = 0; i < sizeof(ui->scr_home_tab_pp_btn_hide) / sizeof(ui->scr_home_tab_pp_btn_hide[i]); ++i)
    {
        lv_obj_set_event_cb(ui->scr_home_tab_pp_btn_hide[i], event_cb_btn_hide);
    }
}

//========================================================Screen_M0_M4========================================================//
/**
 * @brief   M0 / M4 Show 页退出按钮回调
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_imgbtn_quit(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_RELEASED) /* 任何情况下对象释放了就触发 */
    {
        /* 清理页面, 释放资源 */
        page_clean_m0_m4();

        /* 枚举值等间距: M0_SHOW or M4_SHOW 均与 M0 or M4 相差 4 */
        LU->scr_switch_flag -= 4;
        if (obj == LU->scr_m0_m4_imgbtn_home) /* 跳转 TAB-Home 页 */
        {
            LU->scr_switch_flag = HOME;
            lv_tabview_set_tab_act(LU->scr_home_tabview, LU->scr_switch_flag, LV_ANIM_OFF);
        }
        /* 单击音效 */
        PLAY_MUSIC(0);
    }
}

/**
 * @brief   M0 / M4 Show 页开关回调(全屏跟随触摸)
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_switch_video(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_VALUE_CHANGED)
    {
        Video_Touch_Flag = (lv_switch_get_state(obj)) ? 1 : 0;
        /* 回到中间，避免视觉上遮挡按钮 */
        if (Video_Touch_Flag == 0) 
        {
            video_set_post(LAYER_2_VIDEO,
                           GET_OFFSET_X(LV_HOR_RES_MAX, FLASH_VIDEO_AREA_W, 1),
                           GET_OFFSET_Y(LV_VER_RES_MAX, FLASH_VIDEO_AREA_H, 1));
        }
    }
}

/**
 * @brief   M0 / M4 Show 页滑块回调(视频图层透明度调节)
 * @param   obj : 触发对象
 * @param   e   : 事件类型
 * @retval  \
 */
static void event_cb_slider_video(lv_obj_t *obj, lv_event_t e)
{
    if (e == LV_EVENT_VALUE_CHANGED) /* 滑块值被改变 */
    {
        lcd_set_alpha(LAYER_2_VIDEO, 0xFF * lv_slider_get_value(obj) / 100);
    }
}

/**
 * @brief   M0 / M4 芯片具体型号介绍展示页注册事件回调
 * @param   ui : 全局句柄
 * @retval  \
 */
void events_init_scr_m0_m4(lv_ui *ui)
{
    /* 注册 Home / Back 键的事件回调 */
    lv_obj_set_event_cb(ui->scr_m0_m4_imgbtn_home, event_cb_imgbtn_quit);
    lv_obj_set_event_cb(ui->scr_m0_m4_imgbtn_back, event_cb_imgbtn_quit);

    lv_obj_set_event_cb(ui->scr_m0_m4_sw_video, event_cb_switch_video);
    lv_obj_set_event_cb(ui->scr_m0_m4_slider_video, event_cb_slider_video);
}

//========================================================GIF演示========================================================//
/**
 * @brief   GIF 轮播刷新任务(用于 M0 芯片素材显示)
 * @param   t : 任务句柄
 * @retval  \
 */
static void gif_task_m0(lv_task_t *t)
{
    uint16_t index = GIF.i;
    switch (*(uint8_t *)(t->user_data)) /* 在多次执行且分支数更多时 switch 比 if / else 速度更快 */
    {
    case (SWM_160): /* 素材缺失 */
    {
        index += M0_GIF_181_START;
        GIF.img = Image_Table[index].img;
        GIF.i_max = M0_GIF_181_SIZE;
        break;
    }
    case (SWM_181):
    {
        index += M0_GIF_181_START;
        GIF.img = Image_Table[index].img;
        GIF.i_max = M0_GIF_181_SIZE;
        break;
    }
    case (SWM_190):
    {
        index += M0_GIF_190_START;
        GIF.img = Image_Table[index].img;
        GIF.i_max = M0_GIF_190_SIZE;
        break;
    }
    case (SWM_201):
    {
        index += M0_GIF_201_START;
        GIF.img = Image_Table[index].img;
        GIF.i_max = M0_GIF_201_SIZE;
        break;
    }
    case (SWM_220): /* 素材缺失 */
    {
        index += M0_GIF_181_START;
        GIF.img = Image_Table[index].img;
        GIF.i_max = M0_GIF_181_SIZE;
        break;
    }
    case (SWM_260):
    {
        index += M0_GIF_260_START;
        GIF.img = Image_Table[index].img;
        GIF.i_max = M0_GIF_260_SIZE;
        break;
    }
    default:
        break;
    }

    if (0 == load_source_to_sdram(SPI, Image_Table[index].file->name, IMG, GIF.img, sdram_malloc))
    {
        lv_img_set_src(GIF.obj, GIF.img);
        if (++GIF.i >= GIF.i_max)
            GIF.i = 0;

        LOG_INFO("[remove] img_addr = 0x[%p]\r\n", GIF.img->data);
        remove_source_to_sdram(IMG, (void *)GIF.img, sdram_free);
    }
    else 
    {
        LOG_INFO("sdram_get_idle_size = [%d]\r\n", sdram_get_idle_size());
    }
    lv_obj_align(GIF.obj, NULL, LV_ALIGN_CENTER, 0, 0);
}

/**
 * @brief   GIF 轮播刷新任务(用于 M4 芯片素材显示)
 * @param   t : 任务句柄
 * @retval  \
 */
static void gif_task_m4(lv_task_t *t)
{
    uint16_t index = GIF.i;
    switch (*(uint8_t *)(t->user_data))
    {
    case (SWM_320):
    {
        index += M4_GIF_320_START;
        GIF.img = Image_Table[index].img;
        GIF.i_max = M4_GIF_320_SIZE;
        break;
    }
    case (SWM_341): /* 素材缺失 */
    {
        index += M4_GIF_320_START;
        GIF.img = Image_Table[index].img;
        GIF.i_max = M4_GIF_320_SIZE;
        break;
    }
    default:
        break;
    }

    if (0 == load_source_to_sdram(SPI, Image_Table[index].file->name, IMG, GIF.img, sdram_malloc))
    {
        lv_img_set_src(GIF.obj, GIF.img);
        if (++GIF.i >= GIF.i_max)
            GIF.i = 0;

        LOG_INFO("[remove] img_addr = 0x[%p]\r\n", GIF.img->data);
        remove_source_to_sdram(IMG, (void *)GIF.img, sdram_free);
    }
    else 
    {
        LOG_INFO("sdram_get_idle_size = [%d]\r\n", sdram_get_idle_size());
    }
    lv_obj_align(GIF.obj, NULL, LV_ALIGN_CENTER, 0, 0);
}

/**
 * @brief   产品应用演示 GIF 轮播刷新任务
 * @param   t : 任务句柄
 * @retval  \
 */
static void gif_task_pd(lv_task_t *t)
{
    /** 页面刷新标志    0\默认GIF图          1\全屏刷新测试图 */
    static volatile uint8_t Flag_Srceen_IMG = 0;
    /** 页面刷新速度    0\默认速度 1s/张     1\无间隔一直刷图 */
	static volatile uint16_t Flag_Srceen_Period = 0;

    uint16_t index = GIF.i;

    if (0 == Flag_Srceen_IMG) /* 局部刷新 */
    {
        index += PD_GIF_START;
        GIF.img = Image_Table[index].img;
        GIF.i_max = PD_GIF_SIZE;
        if (0 == load_source_to_sdram(SPI, Image_Table[index].file->name, IMG, GIF.img, sdram_malloc))
        {
            lv_img_set_src(GIF.obj, GIF.img);
            // GIF.index = (GIF.index < GIF.index_max) ? GIF.index : 0;

            LOG_INFO("[remove] img_addr = 0x[%p]\r\n", GIF.img->data);
            remove_source_to_sdram(IMG, (void *)GIF.img, sdram_free);
        }
        else 
        {
            LOG_INFO("sdram_get_idle_size = [%d]\r\n", sdram_get_idle_size());
        }

        if (++GIF.i >= GIF.i_max)
        {
            GIF.i = 0;
            Flag_Srceen_IMG = 1;
        }
    }
    else /* 全屏刷新 */
    {
        index += FULL_SCR_IMG_START;
        GIF.img = Image_Table[index].img;
        GIF.i_max = FULL_SCR_IMG_SIZE;
        if (0 == load_source_to_sdram(SPI, Image_Table[index].file->name, IMG, GIF.img, sdram_malloc))
        {
            lv_img_set_src(GIF.obj, GIF.img);
            // GIF.index = (GIF.index < GIF.index_max) ? GIF.index : 0;

            LOG_INFO("[remove] img_addr = 0x[%p]\r\n", GIF.img->data);
            remove_source_to_sdram(IMG, (void *)GIF.img, sdram_free);
			/* 发送到后台作背景 */
            lv_obj_move_background(GIF.obj);
        }
        else 
        {
            LOG_INFO("sdram_get_idle_size = [%d]\r\n", sdram_get_idle_size());
        }
        
        if (++GIF.i >= GIF.i_max)
        {
            GIF.i = 0;
            Flag_Srceen_IMG = 0;
			/* 设置新的任务调用周期 */
            lv_task_set_period(GIF.task, Flag_Srceen_Period); 
            Flag_Srceen_Period = (0 != Flag_Srceen_Period) ? 0 : Task_Ms_PD;
        }
    }

    lv_obj_align(GIF.obj, NULL, LV_ALIGN_CENTER, 0, 0);
}

/**
 * @brief   中文字体 与 GIF 轮播演示 任务
 * @param   t : 任务句柄
 * @retval  \
 */
static void gif_task_pp(lv_task_t *t)
{
    uint16_t index = GIF.i;
    index += PP_GIF_START;
    GIF.img = Image_Table[index].img;
    GIF.i_max = PP_GIF_SIZE;

#if GIF_SHOW_MODE
    if (0 == load_source_to_sdram(SPI, Image_Table[index].file->name, IMG, GIF.img, sdram_malloc))
    {
#else
    {
#endif

        lv_img_set_src(GIF.obj, GIF.img);
        if (++GIF.i >= GIF.i_max)
            GIF.i = 0;

#if GIF_SHOW_MODE
        LOG_INFO("[sdram_free] addr = 0x[%p]\r\n", GIF.img->data);
        remove_source_to_sdram(IMG, (void *)GIF.img, sdram_free);
#endif
    }
    lv_obj_align(GIF.obj, NULL, LV_ALIGN_CENTER, 0, 0);
}
